﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEditor;

namespace WentStateMachine
{
    public class LuaStateMachineControllerDataExporter : IStateMachineControllerGUIEx
    {
        private TextAsset fileToOutput;
        private const string scripts = @"
{0}

return controller
";

        public void Generate(StateMachineController controller)
        {
            ControllerGenerator cg;
            string result = string.Format(scripts, cg.Generate(controller));
            string path = fileToOutput ? AssetDatabase.GetAssetPath(fileToOutput) : AssetDatabase.GetAssetPath(controller);
            path = Path.GetDirectoryName(path) + "/controller.txt";
            FileStream stream = File.Open(path, FileMode.OpenOrCreate, FileAccess.Write);
            stream.Seek(0, SeekOrigin.Begin);
            stream.SetLength(0);
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(result);
            writer.Flush();
            writer.Close();
        }

        public void OnGUI(StateMachineController controller)
        {
            EditorGUILayout.BeginVertical();
            EditorGUILayout.LabelField("Lua data generate EX");
            if(GUILayout.Button("Generate Lua Data"))
            {
                Generate(controller);
            }

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.LabelField("FileToOutput:",GUILayout.Width(70f));
            fileToOutput = (TextAsset)EditorGUILayout.ObjectField(fileToOutput, typeof(TextAsset), false);
            EditorGUILayout.EndHorizontal();
            EditorGUILayout.EndVertical();
        }
    }

    struct ControllerGenerator
    {
        const string Temp = @"
local controller = {{
    [""layers""] = {{
        {0}
    }},
    [""parameters""] = {{
        {1}
    }}
}}";

        public string Generate(StateMachineController controller)
        {
            StringBuilder builder = new StringBuilder();

            LuaLayerGenerator lg;
            string layers = "";
            if(controller.layers != null)
            {
                foreach (var layer in controller.layers)
                    builder.AppendLine(lg.Generate(layer));
                layers = builder.ToString();
                builder.Clear();
            }

            ParameterGenerator pm;
            string parameters = "";
            if(controller.parameters != null)
            {
                foreach (var param in controller.parameters)
                    builder.AppendLine(pm.Generate(param));
                parameters = builder.ToString();
            }

            return string.Format(Temp, layers, parameters);
        }
    }

    struct ParameterGenerator
    {
        const string Temp = @"
        {{
            [""type""] = {0},
            [""nameHash""] = {1},
            [""defaultBool""] = {2},
            [""defaultNumber""] = {3},
        }},";

        public string Generate(Parameter parameter)
        {
            if(parameter.type == ParameterType.Float)
            {
                return string.Format(Temp, (int)parameter.type, parameter.nameHash, 
                    parameter.defaultBool ? "true":"false", parameter.defaultFloat);
            }
            else if(parameter.type == ParameterType.Int)
            {
                return string.Format(Temp, (int)parameter.type, parameter.nameHash,
                    parameter.defaultBool ? "true" : "false", parameter.defaultInt);
            }
            else
            {
                return string.Format(Temp, (int)parameter.type, parameter.nameHash,
                    parameter.defaultBool ? "true" : "false", 0);
            }
        }
    }

    struct ConditionGenerator
    {
        const string Temp = @"
        {{
            [""parameterHash""] = {0},
            [""threshold""] = {1},
            [""mode""] = {2}
        }},";

        public string Generate(Condition condition)
        {
            return string.Format(Temp, condition.parameterHash, condition.threshold, 
                (int)condition.mode);
        }
    }

    struct IntArrayGenerator
    {
        public string Generate(int[] array)
        {
            return $"{{{string.Join(",", array)}}}";
        }
    }

    struct TransitionGenerator
    {
        const string Temp = @"
        {{
            [""destinationState""] = {0},
            [""destinationStateMachine""] = {1},
            [""conditions""] = {{
                {2}
            }},
        }},";

        public string Generate(Transition transition)
        {
            IntArrayGenerator ig;
            string destinationState = transition.destinationState == null ?
                "{}" : ig.Generate(transition.destinationState);
            string destinationStateMachine =
                transition.destinationStateMachine == null ?
                "{}" : ig.Generate(transition.destinationStateMachine);

            if(transition.conditions == null)
            {
                return string.Format(Temp, destinationState, destinationStateMachine, "");
            }

            ConditionGenerator cg;
            StringBuilder conditions = new StringBuilder();
            foreach(var condition in transition.conditions)
            {
                conditions.AppendLine(cg.Generate(condition));
            }
            return string.Format(Temp, destinationState, destinationStateMachine, conditions.ToString());
        }
    }

    struct StateBehaviourGenerator
    {
        public string Generate(StateBehaviour stateBehaviours)
        {
            LuaStateBehaviour luaState = stateBehaviours as LuaStateBehaviour;
            if (luaState == null)
                throw new InvalidOperationException("Only LuaStateBehaviour can convert to lua data.");
            return $"\"{luaState.GetScriptNameWithoutExtension()}\",";
        }
    }

    struct StateGenerator
    {
        const string Temp = @"
                {{
                    [""nameHash""] = {0},
                    [""StateBehaviours""] = {1},
                    [""transitions""] = {{
                        {2}
                    }},
                }},";

        public string Generate(State state)
        {
            StringBuilder builder = new StringBuilder();
            StateBehaviourGenerator sg;
            string stateBehaviours = "{}";
            if(state.behaviours != null)
            {
                
                builder.Append("{");
                foreach (var be in state.behaviours)
                {
                    builder.Append(sg.Generate(be));
                }
                builder.Append("}");
                stateBehaviours = builder.ToString();
                builder.Clear();
            }

            TransitionGenerator tg;
            string transitions = "";
            if(state.Transitions != null)
            {
                foreach(var tran in state.Transitions)
                {
                    builder.AppendLine(tg.Generate(tran));
                }
                transitions = builder.ToString();
            }
            return string.Format(Temp, state.nameHash, stateBehaviours, transitions);
        }
    }

    struct StateMachineGenerator
    {
        const string Temp = @"
            {{
                [""defaultState""] = {0},
                [""nameHash""] = {1},
                [""anyStateTransition""] = {{
                    {2}
                }},
                [""entryTransition""] = {{
                    {3}
                }},
                [""states""] = {{
                    {4}
                }},
                [""childStateMachine""] = {{
                    {5}
                }},
            }},";

        public string Generate(StateMachine stateMachine){
            StringBuilder builder = new StringBuilder();

            TransitionGenerator tg;
            string anyStateTransition = "";
            if(stateMachine.AnyStateTransition != null)
            {
                foreach (var tran in stateMachine.AnyStateTransition)
                    builder.AppendLine(tg.Generate(tran));
                anyStateTransition = builder.ToString();
                builder.Clear();
            }

            string entryTransition = "";
            if (stateMachine.EntryTransition != null)
            {
                foreach (var tran in stateMachine.EntryTransition)
                    builder.AppendLine(tg.Generate(tran));
                entryTransition = builder.ToString();
                builder.Clear();
            }

            StateGenerator sg;
            string states = "";
            if (stateMachine.States != null)
            {
                foreach (var state in stateMachine.States)
                    builder.AppendLine(sg.Generate(state));
                states = builder.ToString();
                builder.Clear();
            }

            StateMachineGenerator stg;
            string childStateMachine = "";
            if(stateMachine.ChildStateMachine != null)
            {
                foreach (var cm in stateMachine.ChildStateMachine)
                    builder.AppendLine(stg.Generate(cm));
                childStateMachine = builder.ToString();
            }

            return string.Format(Temp, stateMachine.DefaultState, stateMachine.nameHash,
                anyStateTransition, entryTransition, states, childStateMachine);
        }
    }

    struct LuaLayerGenerator
    {
        const string Temp = @"
        {{
            [""nameHash""] = {0},
            [""stateMachine""] = {1}
        }},";

        public string Generate(Layer layer)
        {
            StateMachineGenerator smg;
            string stateMachine = layer.stateMachine == null ?
                "nil" : smg.Generate(layer.stateMachine);
            return string.Format(Temp, layer.nameHash, stateMachine);
        }
    }
}
